﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Office = Microsoft.Office.Core;
using MSProject = Microsoft.Office.Interop.MSProject;
using System.Windows.Forms;
using System.Collections;
using r2mp_DataStructures;
using r2mp_GUI;

namespace r2mp_Project
{
    /// <summary>
    /// The r2mpRibbonsTab class provides all core-functionality to enable communication and data-extraction
    /// from Microsoft Project.
    /// 
    /// This class also provides the backend functionality of all other ribbons-controls generated
    /// by the addin into the ribbons-menu of Microsoft-Project.
    /// </summary>
    [ComVisible(true)]
    public class r2mpRibbonsTab : Office.IRibbonExtensibility
    {
        private Office.IRibbonUI ribbon;


        /// <summary>
        /// Creates an example-PSP and uses it to test the PSP-Visualizer-Module.
        /// This method is not used anymore and only exists for testing and debugging-pruposes.
        /// </summary>
        /// <param name="control">The control object supplied by the calling Ribbon-Application</param>
        public void testPSP(Office.IRibbonControl control)
        {
            new R2mpVisualizer("Projektstrukturplan Test", PSPTest.createTestPSP()).ShowDialog();
        }

        /// <summary>
        /// Calls the necessary methods to create the PSP-Diagram from the currently opened Project
        /// inside Microsoft Project.
        /// </summary>
        /// <param name="control">The control object supplied by the calling Ribbon-Application</param>
        public void doPSP(Office.IRibbonControl control)
        {
            new R2mpVisualizer("Projektstrukturplan", PSP()).ShowDialog();
        }

        /// <summary>
        /// Analyzes the currently opened project and puts all the tasks inside the project into
        /// a tree made up of Task-Objects. The depth at which the nodes are inserted is determined
        /// by the numbers the tasks have been assigned by Microsoft Project.
        /// Milestones (tasks with a duration equal 0) are filtered from this tree.
        /// </summary>
        /// <returns>A Task-Object which functions as the root of the whole internal tree
        /// representing the PSP inside the application.</returns>
        private Task PSP()
        {
            Task tasklist = new Task();
            Task listPosition = tasklist;
            Task newTask;
            String[] tasknumber;
            MSProject.Tasks tasks = Globals.ThisAddIn.Application.ActiveProject.Tasks;
            tasklist.Name = Globals.ThisAddIn.Application.ActiveProject.ProjectSummaryTask.Name;
            foreach (MSProject.Task currentTask in tasks)
            {
                tasknumber = currentTask.OutlineNumber.Split('.');
                //MessageBox.Show(currentTask.OutlineNumber); //DEBUG CODE
                //MessageBox.Show(currentTask.OutlineNumber); //DEBUG CODE

                //Traversing the tree level by level until the right depth and the right parent-node
                //for the node to be inserted is found.
                for (int i = 0; i < tasknumber.Length - 1; i++)
                {
                    listPosition = listPosition.Tasks[Int32.Parse(tasknumber[i]) - 1];
                }
                newTask = new Task();
                newTask.Duration = currentTask.Duration;
                newTask.Name = currentTask.Name;
                newTask.Nr = currentTask.OutlineNumber;
                newTask.Ressources = new List<string>();
                foreach (MSProject.Resource currentResource in currentTask.Resources)
                {
                    newTask.Ressources.Add(currentResource.Name);
                }
                if (currentTask.Duration == 0)
                {
                    newTask.Status = Task.Milestone;
                }
                else if (currentTask.PercentComplete == 0)
                {
                    newTask.Status = Task.Started;
                }
                else if (currentTask.PercentComplete > 0 && currentTask.PercentComplete < 100)
                {
                    newTask.Status = Task.Pending;
                }
                else if (currentTask.PercentComplete == 100)
                {
                    newTask.Status = Task.Finished;
                }
                listPosition.Tasks.Add(newTask);
                listPosition = tasklist;
            }
            //tasklist.Name = Globals.ThisAddIn.Application.Name; //DEBUG CODE
            return tasklist;
            //showTasks(tasklist); //DEBUG CODE
        }

        /// <summary>
        /// Calls the necessary methods to create the MTA-Diagram from the currently opened Project
        /// inside Microsoft Project.
        /// </summary>
        /// <param name="control">The control object supplied by the calling Ribbon-Application</param>
        public void doMTA(Office.IRibbonControl control)
        {
            new r2mp_GUI.R2mpVisualizer("Meilenstein Trendanalyse", MTA(30)).ShowDialog();
        }

        /// <summary>
        /// Analyzes the currently opened project to create an MTA. For this purpose
        /// all tasks are traversed and only milestones (tasks with a duration equal 0)
        /// are extracted. The different assumed completion dates for the milestones
        /// can be extracted from the Microsoft-Project-fields BaseLineStart1-BaseLineStart10.
        /// </summary>
        /// <param name="period">States if the data is to be interpreted in weeks or months.
        /// Constants for this varaible are found in DataStructures.MTA</param>
        /// <returns></returns>
        private MTA MTA(int period)
        {
            MSProject.Tasks tasks = Globals.ThisAddIn.Application.ActiveProject.Tasks;
            List<Milestone> milestoneList = new List<Milestone>();
            DateTime[] offsetCompletionDates = new DateTime[10];
            foreach (MSProject.Task currentTask in tasks)
            {
                if (currentTask.Duration != 0) continue;
                DateTime plannedCompletionDate = dynamicDateTimeFilter(currentTask.BaselineStart);
                if (plannedCompletionDate == DateTime.MinValue) continue;
                DateTime previousDate = plannedCompletionDate;
                Milestone currentMilestone = new Milestone(currentTask.Name, plannedCompletionDate);
                offsetCompletionDates[0] = dynamicDateTimeFilter(currentTask.Baseline1Start);
                offsetCompletionDates[1] = dynamicDateTimeFilter(currentTask.Baseline2Start);
                offsetCompletionDates[2] = dynamicDateTimeFilter(currentTask.Baseline3Start);
                offsetCompletionDates[3] = dynamicDateTimeFilter(currentTask.Baseline4Start);
                offsetCompletionDates[4] = dynamicDateTimeFilter(currentTask.Baseline5Start);
                offsetCompletionDates[5] = dynamicDateTimeFilter(currentTask.Baseline6Start);
                offsetCompletionDates[6] = dynamicDateTimeFilter(currentTask.Baseline7Start);
                offsetCompletionDates[7] = dynamicDateTimeFilter(currentTask.Baseline8Start);
                offsetCompletionDates[8] = dynamicDateTimeFilter(currentTask.Baseline9Start);
                offsetCompletionDates[9] = dynamicDateTimeFilter(currentTask.Baseline10Start);
                foreach (DateTime currentDate in offsetCompletionDates)
                {
                    if (currentDate.Equals(DateTime.MinValue)) break;
                    int timeshift = getTimeshift(period, previousDate, currentDate);
                    currentMilestone.Timeshifts.Add(timeshift);
                    currentMilestone.TimeshiftSum += timeshift;
                    previousDate = currentDate;
                }
                milestoneList.Add(currentMilestone);
            }
            MTA resultMTA = new MTA();
            resultMTA.Milestones = milestoneList;
            resultMTA.Periode = period;
            return resultMTA;
        }

        /// <summary>
        /// Is used to check if the content of a dynamic object is of type DateTime or not.
        /// </summary>
        /// <param name="dynamicContent">The dynamic variable to be checked.</param>
        /// <returns>A DateTime-Object with either the content of the object or DateTime.MinValue if the content
        /// is not of the DateTime-type.</returns>
        private DateTime dynamicDateTimeFilter(dynamic dynamicContent)
        {
            if (dynamicContent.GetType() == typeof(DateTime))
            {
                return (DateTime)dynamicContent;
            }
            else
            {
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// Gets the number of periods that are different between two supplied dates.
        /// </summary>
        /// <param name="period">The period by which the dates are differed.
        /// Constants for this varaible are found in DataStructures.MTA</param>
        /// <param name="firstDate">The first date of the two dates which should be differed.</param>
        /// <param name="secondDate">The second date of the two dates which should be differed.</param>
        /// <returns></returns>
        private int getTimeshift(int period, DateTime firstDate, DateTime secondDate)
        {
            bool found = false;
            int re = 0;
            //Calculations done on a monthly basis.
            if (period == r2mp_DataStructures.MTA.Month)
            {
                DateTime monthBegin = new DateTime(firstDate.Year, firstDate.Month, 1);
                DateTime monthEnd = new DateTime(secondDate.Year, secondDate.Month, 1);
                DateTime currentMonth = monthBegin;
                if (monthBegin == monthEnd)
                {
                    re = 0;
                }
                else if (monthBegin < monthEnd)
                {
                    //Simply add a month until the date is bigger than the second date and count the number of iterations.
                    while (currentMonth < monthEnd)
                    {
                        re++;
                        currentMonth = currentMonth.AddMonths(1);
                    }
                }
                else
                {
                    //If the second date is earlier than the first one, the difference is still the same, only negative.
                    re = -1 * getTimeshift(period, secondDate, firstDate);
                }
            }
            //Calculations by a weekly basis (written by Oliver Seewald).
            else if(period == r2mp_DataStructures.MTA.Week)
            {
                switch (firstDate.DayOfWeek)
                {
                    case DayOfWeek.Tuesday:
                        firstDate = firstDate.AddDays(-1);
                        break;
                    case DayOfWeek.Wednesday:
                        firstDate = firstDate.AddDays(-2);
                        break;
                    case DayOfWeek.Thursday:
                        firstDate = firstDate.AddDays(-3);
                        break;
                    case DayOfWeek.Friday:
                        firstDate = firstDate.AddDays(-4);
                        break;
                    case DayOfWeek.Saturday:
                        firstDate = firstDate.AddDays(-5);
                        break;
                    case DayOfWeek.Sunday:
                        firstDate = firstDate.AddDays(-6);
                        break;
                }

                if (firstDate.Date > secondDate.Date)
                {
                    re--;
                    while (!found)
                    {
                        if (firstDate.AddDays(-7) < secondDate)
                        {
                            found = true;
                        }
                        else
                        {
                            firstDate = firstDate.AddDays(-7);
                            re--;
                        }
                    }
                }
                else if (firstDate.Date < secondDate.Date)
                {
                    while (!found)
                    {
                        if (firstDate.AddDays(7) > secondDate)
                        {
                            found = true;
                        }
                        else
                        {
                            re++;
                            firstDate = firstDate.AddDays(7);
                        }
                    }
                }
                else
                {
                    found = true;
                }

            }
            return re;
        }

        #region Debug
        public void TestAction(Office.IRibbonControl control)
        {
            MessageBox.Show("Test für r2mp");
        }

        private void showTasks(Task tasklist)
        {
            foreach (Task currentTask in tasklist.Tasks)
            {
                MessageBox.Show(currentTask.Name);
                showTasks(currentTask);
            }
        }
        #endregion

        #region Deprecated
        //private MTA MTA_deprecated(int period)
        //{
        //    MSProject.Tasks tasks = Globals.ThisAddIn.Application.ActiveProject.Tasks;
        //    List<Milestone> milestoneList = new List<Milestone>();
        //    Dictionary<int, DateTime> baselineCompletionDates = new Dictionary<int, DateTime>();
        //    foreach (MSProject.Task currentTask in tasks)
        //    {
        //        if (currentTask.Duration != 0) continue;
        //        MSProject.PjField currentFieldID = Globals.ThisAddIn.Application.FieldNameToFieldConstant("Baseline Start");
        //        DateTime currentDate = DateTime.Parse(currentTask.GetField(currentFieldID));
        //        Milestone currentMilestone;
        //        currentMilestone = new Milestone(currentTask.Name, currentDate);
        //        int currentBaselineID = 1;
        //        currentFieldID = Globals.ThisAddIn.Application.FieldNameToFieldConstant("Baseline" + currentBaselineID.ToString() + " Start");
        //        String currentFieldValue = currentTask.GetField(currentFieldID);
        //        while (currentFieldValue != "NA")
        //        {
        //            DateTime nextDate = DateTime.Parse(currentTask.GetField(currentFieldID));
        //            int ts = getTimeshift(period, currentDate, nextDate);
        //            currentMilestone.Timeshifts.Add(ts);
        //            currentMilestone.TimeshiftSum += ts;
        //            currentDate = nextDate;
        //            currentBaselineID++;
        //            currentFieldID = Globals.ThisAddIn.Application.FieldNameToFieldConstant("Baseline" + currentBaselineID.ToString() + " Start");
        //            currentFieldValue = currentTask.GetField(currentFieldID);
        //        }
        //        milestoneList.Add(currentMilestone);
        //    }
        //    MTA result = new MTA();
        //    result.Milestones = milestoneList;
        //    return result;
        //}


        //private int getTimeshift_deprecated(int period, DateTime firstDate, DateTime secondDate)
        //{
        //    int timeshift = 0;
        //    if (firstDate <= secondDate)
        //    {
        //        while (firstDate < secondDate)
        //        {
        //            firstDate = firstDate.AddDays(period);
        //            timeshift++;
        //        }
        //        return timeshift;
        //    }
        //    else
        //    {
        //        return -1 * getTimeshift_deprecated(period, secondDate, firstDate);
        //    }
        //}
        #endregion

        #region IRibbonExtensibility Members

        public string GetCustomUI(string ribbonID)
        {
            return GetResourceText("r2mp_Project.r2mpRibbonsTab.xml");
        }

        #endregion

        #region Ribbon Callbacks
        //Create callback methods here. For more information about adding callback methods, select the Ribbon XML item in Solution Explorer and then press F1

        public void Ribbon_Load(Office.IRibbonUI ribbonUI)
        {
            this.ribbon = ribbonUI;
        }

        /// <summary>
        /// Is used to supply a r2mp_Excel.Dialog with a delegate for displaying a MTA.
        /// </summary>
        /// <param name="mta">The supplied MTA.</param>
        public void excelMTA(MTA mta) 
        {
            new r2mp_GUI.R2mpVisualizer("Excel Meilenstein Trendanalyse", mta).ShowDialog();
        }
        
        /// <summary>
        /// Is used to supply a r2mp_Excel.Dialog with a delegate for displaying a PSP.
        /// </summary>
        /// <param name="psp">The supplied PSP.</param>
        public void excelPSP(Task psp)
        {
            new r2mp_GUI.R2mpVisualizer("Excel Projektstrukturplan", psp).ShowDialog();
        }

        /// <summary>
        /// Is called by the respective button inside the ribbon-menu.
        /// Opens an r2mp_Excel.Dialog to select files and the diagram to be created.
        /// </summary>
        /// <param name="control">The control supplied by the calling ribbon.</param>
        public void excelDiagrams(Office.IRibbonControl control)
        {
            r2mp_Excel.Dialog excelDialog = new r2mp_Excel.Dialog();
            excelDialog.SetDrawMTA = excelMTA;
            excelDialog.SetDrawPSP = excelPSP;
            excelDialog.ShowDialog();
        }

        /// <summary>
        /// Is called by the respective button inside the ribbon-menu.
        /// Displays a dialog to make all necessary selections about the diagram to be
        /// created from the data inside the currently opened file inside Microsoft Project.
        /// </summary>
        /// <param name="control">The control supplied by the calling ribbon.</param>
        public void projectDiagrams(Office.IRibbonControl control)
        {
            DiagramDialog dia = new DiagramDialog();
            R2mpVisualizer vis = null;
            MTA mta;
            Task psp;
            if (dia.ShowDialog() == DialogResult.OK)
            {
                switch (dia.SelectedDiagram)
                {
                    case DiagramDialog.MTA:
                        mta = MTA(dia.Period);
                        if (mta.Milestones.Count == 0)
                        {
                            MessageBox.Show("Diese Liste enthält keine Meilensteine!", "Achtung!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            break;
                        }
                        vis = new R2mpVisualizer("Meilensteintrendanalyse", mta);
                        vis.Show();
                        break;
                    case DiagramDialog.PSP:
                        psp = PSP();
                        if (psp.Tasks.Count == 0)
                        {
                            MessageBox.Show("Diese Liste enthält keine Arbeitspakete!", "Achtung!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            break;
                        }
                        vis = new R2mpVisualizer("Projektstrukturplan", PSP());
                        vis.Show();
                        break;
                }
            }
        }

        #endregion

        #region Helpers

        private static string GetResourceText(string resourceName)
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            string[] resourceNames = asm.GetManifestResourceNames();
            for (int i = 0; i < resourceNames.Length; ++i)
            {
                if (string.Compare(resourceName, resourceNames[i], StringComparison.OrdinalIgnoreCase) == 0)
                {
                    using (StreamReader resourceReader = new StreamReader(asm.GetManifestResourceStream(resourceNames[i])))
                    {
                        if (resourceReader != null)
                        {
                            return resourceReader.ReadToEnd();
                        }
                    }
                }
            }
            return null;
        }

        #endregion
    }
}
